Return to start page

Core/Environment/Struct Missile.j

Code

		
1			library AStructCoreEnvironmentMissile requires optional ALibraryCoreDebugMisc, AStructCoreGeneralVector, ALibraryCoreMathsHandle, ALibraryCoreMathsPoint, ALibraryCoreInterfaceSelection
2
3 /// OnCollisionFunction functions can be set by method @method setOnCollisionFunction and will be called when missile collides.
4 function interface AMissileTypeOnCollisionFunction takes AMissile missile returns nothing
5
6 /// OnDeathFunction functions can by set by method @method setOnDeathFunction and will be called when missile hits target.
7 function interface AMissileTypeOnDeathFunction takes AMissile missile returns nothing
8
9 struct AMissileType
10 private player m_owner
11 private integer m_unitType
12 private real m_speed
13 private real m_maxHeight
14 private boolean m_collides
15 private boolean m_destroyOnDeath
16 private string m_deathEffectPath
17 private string m_startSoundPath
18 private string m_deathSoundPath
19 private AMissileTypeOnCollisionFunction m_onCollisionFunction
20 private AMissileTypeOnDeathFunction m_onDeathFunction
21
22 public method setOwner takes player owner returns nothing
23 set this.m_owner = owner
24 endmethod
25
26 public method owner takes nothing returns player
27 return this.m_owner
28 endmethod
29
30 public method setUnitType takes integer unitType returns nothing
31 set this.m_unitType = unitType
32 endmethod
33
34 public method unitType takes nothing returns integer
35 return this.m_unitType
36 endmethod
37
38 public method setSpeed takes real speed returns nothing
39 set this.m_speed = speed * AMissile.refreshTime()
40 endmethod
41
42 public method speed takes nothing returns real
43 return this.m_speed
44 endmethod
45
46 public method setMaxHeight takes real speed returns nothing
47 set this.m_speed = speed * AMissile.refreshTime()
48 endmethod
49
50 public method maxHeight takes nothing returns real
51 return this.m_maxHeight
52 endmethod
53
54 public method setCollides takes boolean collides returns nothing
55 set this.m_collides = collides
56 endmethod
57
58 public method collides takes nothing returns boolean
59 return this.m_collides
60 endmethod
61
62 public method setDestroyOnDeath takes boolean destroyOnDeath returns nothing
63 set this.m_destroyOnDeath = destroyOnDeath
64 endmethod
65
66 public method destroyOnDeath takes nothing returns boolean
67 return this.m_destroyOnDeath
68 endmethod
69
70 public method setDeathEffectPath takes string deathEffectPath returns nothing
71 set this.m_deathEffectPath = deathEffectPath
72 endmethod
73
74 public method deathEffectPath takes nothing returns string
75 return this.m_deathEffectPath
76 endmethod
77
78 public method setStartSoundPath takes string startSoundPath returns nothing
79 set this.m_startSoundPath = startSoundPath
80 endmethod
81
82 public method startSoundPath takes nothing returns string
83 return this.m_startSoundPath
84 endmethod
85
86 public method setDeathSoundPath takes string deathSoundPath returns nothing
87 set this.m_deathSoundPath = deathSoundPath
88 endmethod
89
90 public method deathSoundPath takes nothing returns string
91 return this.m_deathSoundPath
92 endmethod
93
94 public method setOnCollisionFunction takes AMissileTypeOnCollisionFunction onCollisionFunction returns nothing
95 set this.m_onCollisionFunction = onCollisionFunction
96 endmethod
97
98 public method onCollisionFunction takes nothing returns AMissileTypeOnCollisionFunction
99 return this.m_onCollisionFunction
100 endmethod
101
102 public method setOnDeathFunction takes AMissileTypeOnDeathFunction onDeathFunction returns nothing
103 set this.m_onDeathFunction = onDeathFunction
104 endmethod
105
106 public method onDeathFunction takes nothing returns AMissileTypeOnDeathFunction
107 return this.m_onDeathFunction
108 endmethod
109
110 public static method create takes nothing returns thistype
111 local thistype this = thistype.allocate()
112 //dynamic members
113 set this.m_owner = null
114 set this.m_unitType = 'hfoo'
115 set this.m_speed = 100.0
116 set this.m_maxHeight = 400.0
117 set this.m_collides = false
118 set this.m_destroyOnDeath = false
119 set this.m_deathEffectPath = null
120 set this.m_startSoundPath = null
121 set this.m_deathSoundPath = null
122 set this.m_onCollisionFunction = 0
123 set this.m_onDeathFunction = 0
124 return this
125 endmethod
126
127 public method onDestroy takes nothing returns nothing
128 //dynamic members
129 set this.m_owner = null
130 endmethod
131 endstruct
132
133 private function ADamageMissileTypeOnDeathFunctionDamage takes AMissile missile returns nothing
134 local ADamageMissileType this = ADamageMissileType(missile.missileType())
135 if (this.damage() <= 0.0) then
136 return
137 endif
138 if (this.damageRange() > 0.0) then
139 call UnitDamagePoint(this.damageSource(), 0.0, this.damageRange(), GetUnitX(missile.unit()), GetUnitY(missile.unit()), this.damage(), true, false, this.attackType(), this.damageType(), this.weaponType())
140 //cause area damage to units who aren't allies of the source unit
141 elseif (missile.targetWidget() != null) then
142 call UnitDamageTarget(this.damageSource(), missile.targetWidget(), this.damage(), true, false, this.attackType(), this.damageType(), this.weaponType())
143 //cause single target damage if missile hits widget otherwise show floating text?
144 endif
145 endfunction
146
147 struct ADamageMissileType extends AMissileType
148 //dynamic members
149 private real m_damage
150 private real m_damageRange
151 private unit m_damageSource
152 private attacktype m_attackType
153 private damagetype m_damageType
154 private weapontype m_weaponType
155
156 public method setDamage takes real damage returns nothing
157 set this.m_damage = damage
158 endmethod
159
160 public method damage takes nothing returns real
161 return this.m_damage
162 endmethod
163
164 public method setDamageRange takes real damageRange returns nothing
165 set this.m_damageRange = damageRange
166 endmethod
167
168 public method damageRange takes nothing returns real
169 return this.m_damageRange
170 endmethod
171
172 public method setDamageSource takes unit damageSource returns nothing
173 set this.m_damageSource = damageSource
174 endmethod
175
176 public method damageSource takes nothing returns unit
177 return this.m_damageSource
178 endmethod
179
180 public method setAttackType takes attacktype attackType returns nothing
181 set this.m_attackType = attackType
182 endmethod
183
184 public method attackType takes nothing returns attacktype
185 return this.m_attackType
186 endmethod
187
188 public method setDamageType takes damagetype damageType returns nothing
189 set this.m_damageType = damageType
190 endmethod
191
192 public method damageType takes nothing returns damagetype
193 return this.m_damageType
194 endmethod
195
196 public method setWeaponType takes weapontype weaponType returns nothing
197 set this.m_weaponType = weaponType
198 endmethod
199
200 public method weaponType takes nothing returns weapontype
201 return this.m_weaponType
202 endmethod
203
204 public static method create takes nothing returns thistype
205 local thistype this = thistype.allocate()
206 //dynamic members
207 set this.m_damage = 0.0
208 set this.m_damageRange = 0.0
209 set this.m_damageSource = null
210 set this.m_attackType = ATTACK_TYPE_NORMAL
211 set this.m_damageType = DAMAGE_TYPE_NORMAL
212 set this.m_weaponType = WEAPON_TYPE_WHOKNOWS
213
214 call this.setOnDeathFunction(ADamageMissileTypeOnDeathFunctionDamage)
215 return this
216 endmethod
217
218 public method onDestroy takes nothing returns nothing
219 //dynamic members
220 set this.m_damageSource = null
221 set this.m_attackType = null
222 set this.m_damageType = null
223 set this.m_weaponType = null
224 endmethod
225 endstruct
226
227 /**
228 * Provides the functionality of a single physical missile which is able to cause damage and to have a widget source and target or three coordinate values (x, y and z).
229 * @todo Incompleted!
230 * @todo Add static methods for missile containers.
231 * @todo Collision between missiles?!
232 * @author Draculark
233 * @author Tamino Dauth
234 * @link http://warcraft.ingame.de/forum/showthread.php?s=6f44abe813a621c950b94373b91ed929&threadid=186184
235 */
236 struct AMissile
237 //static start members
238 private static real m_refreshTime
239 private static boolean enableCollisions
240 //static members
241 private static AIntegerVector m_missiles
242 private static timer m_refreshTimer
243 //dynamic members
244 private AMissileType m_missileType
245 private real m_targetX
246 private real m_targetY
247 private widget m_targetWidget
248 private boolean m_isPaused
249 //members
250 private unit m_unit
251 private real m_distance
252 private integer m_index
253
254 //! runtextmacro optional A_STRUCT_DEBUG("\"AMissile\"")
255
256 //dynamic members
257
258 public method setMissileType takes AMissileType missileType returns nothing
259 set this.m_missileType = missileType
260 endmethod
261
262 public method missileType takes nothing returns AMissileType
263 return this.m_missileType
264 endmethod
265
266 public method setTargetX takes real targetX returns nothing
267 set this.m_targetX = targetX
268 endmethod
269
270 public method targetX takes nothing returns real
271 return this.m_targetX
272 endmethod
273
274 public method setTargetY takes real targetY returns nothing
275 set this.m_targetY = targetY
276 endmethod
277
278 public method targetY takes nothing returns real
279 return this.m_targetY
280 endmethod
281
282 public method setTargetWidget takes widget targetWidget returns nothing
283 set this.m_targetWidget = targetWidget
284 endmethod
285
286 public method targetWidget takes nothing returns widget
287 return this.m_targetWidget
288 endmethod
289
290 public method setPaused takes boolean isPaused returns nothing
291 set this.m_isPaused = isPaused
292 endmethod
293
294 public method isPaused takes nothing returns boolean
295 return this.m_isPaused
296 endmethod
297
298 //members
299
300 public method unit takes nothing returns unit
301 return this.m_unit
302 endmethod
303
304 public method distance takes nothing returns real
305 return this.m_distance
306 endmethod
307
308 //convenience methods
309
310 public method startFromUnit takes unit usedUnit returns nothing
311 call this.start(GetUnitX(usedUnit), GetUnitY(usedUnit), GetUnitZ(usedUnit), GetUnitFacing(usedUnit)) //ALibraryMathsHandle
312 endmethod
313
314 /// Makes the missile unpaused which means that it will be moved next time when the periodic trigger moves all unpaused missiles.
315 public method continue takes nothing returns nothing
316 set this.m_isPaused = true
317 endmethod
318
319 /// A paused missile won't be moved until it gets unpaused.
320 public method pause takes nothing returns nothing
321 set this.m_isPaused = false
322 endmethod
323
324 //public methods
325
326 /// Starts the missile from coordinates @param x, @param y, and @param z with angle @param angle.
327 public method start takes real x, real y, real z, real angle returns nothing
328 debug if (this.m_unit != null) then
329 debug call this.print("Missile has already been started.")
330 debug return
331 debug endif
332 debug if (this.m_missileType == 0) then
333 debug call this.print("Can't start with missile type 0.")
334 debug endif
335 set this.m_unit = CreateUnit(this.m_missileType.owner(), this.m_missileType.unitType(), x, y, angle)
336 call SetUnitInvulnerable(this.m_unit, true)
337 call MakeUnitSelectable(this.m_unit, false) //ALibraryInterfaceSelection
338 call SetUnitZ(this.m_unit, z) //ALibraryMathsHandle
339 set this.m_isPaused = false
340 if (this.m_targetWidget != null) then
341 set this.m_distance = GetDistanceBetweenPoints(GetUnitX(this.m_unit), GetUnitY(this.m_unit), 0.0, GetWidgetX(this.m_targetWidget), GetWidgetY(this.m_targetWidget), 0.0)
342 else
343 set this.m_distance = GetDistanceBetweenPoints(GetUnitX(this.m_unit), GetUnitY(this.m_unit), 0.0, this.m_targetX, this.m_targetY, 0.0)
344 endif
345 if (this.m_missileType.startSoundPath() != null) then
346 call PlaySound(this.m_missileType.startSoundPath()) /// @todo use coordinates!
347 endif
348 endmethod
349
350 /// Stops the missile. This means that the missile will be destroyed, damage will be distributed, a death effect will be shown, a death sound will be played and the death function will be executed.
351 public method stop takes nothing returns nothing
352 local effect createdEffect
353 if (this.m_missileType.deathEffectPath() != null) then
354 set createdEffect = AddSpecialEffect(this.m_missileType.deathEffectPath(), GetUnitX(this.m_unit), GetUnitY(this.m_unit))
355 call DestroyEffect(createdEffect)
356 set createdEffect = null
357 endif
358 if (this.m_missileType.deathSoundPath() != null) then
359 call PlaySound(this.m_missileType.deathSoundPath()) /// @todo use coordinates!
360 endif
361 call KillUnit(this.m_unit)
362 call RemoveUnit(this.m_unit)
363 set this.m_unit = null
364 set this.m_isPaused = true
365 if (this.m_missileType.onDeathFunction() != 0) then
366 call this.m_missileType.onDeathFunction().execute(this)
367 endif
368 if (this.m_missileType.destroyOnDeath()) then
369 call this.destroy()
370 endif
371 endmethod
372
373 //private methods
374
375 private method move takes nothing returns nothing
376 local rect mapRect = GetPlayableMapRect()
377 local real currentX = GetUnitX(this.m_unit)
378 local real currentY = GetUnitY(this.m_unit)
379 local real currentDistance
380 local real angle
381 local real cos
382 local real sin
383 local real newX
384 local real newY
385 local real newZ
386 if (this.m_targetWidget != null) then
387 set currentDistance = GetDistanceBetweenPoints(currentX, currentY, 0.0, GetWidgetX(this.m_targetWidget), GetWidgetY(this.m_targetWidget), 0.0)
388 set angle = Atan2(GetWidgetY(this.m_targetWidget) - currentY, GetWidgetX(this.m_targetWidget) - currentX)
389 else
390 set currentDistance = GetDistanceBetweenPoints(currentX, currentY, 0.0, this.m_targetX, this.m_targetY, 0.0)
391 set angle = Atan2(this.m_targetY - currentY, this.m_targetX - currentX)
392 endif
393 set cos = Cos(angle)
394 set sin = Sin(angle)
395 call SetUnitFacing(this.m_unit, angle)
396 set newX = currentX + this.m_missileType.speed() * cos
397 set newY = currentY + this.m_missileType.speed() * sin
398 set newZ = ParabolaZ(this.m_missileType.maxHeight(), this.m_distance, currentDistance)
399
400 if (RectContainsCoords(mapRect, newX, newY) and IsTerrainPathable(newX, newY, PATHING_TYPE_WALKABILITY)) then //not?!
401 call SetUnitX(this.m_unit, newX)
402 call SetUnitY(this.m_unit, newY)
403 call SetUnitZ(this.m_unit, newZ)
404 if (this.m_missileType.collides()) then
405 /// @todo Check for things, maybe other missiles?
406 elseif (this.m_targetWidget != null) then
407 if (newX == GetWidgetX(this.m_targetWidget) and newY == GetWidgetY(this.m_targetWidget)) then
408 call this.stop()
409 endif
410 elseif (newX == this.m_targetX and newY == this.m_targetY) then
411 call this.stop()
412 endif
413 else
414 call this.stop()
415 endif
416 set mapRect = null
417 endmethod
418
419 public static method create takes nothing returns AMissile
420 local thistype this = thistype.allocate()
421 //dynamic members
422 set this.m_missileType = 0
423 //members
424 set this.m_unit = null
425 set this.m_isPaused = true
426 call thistype.m_missiles.pushBack(this)
427 set this.m_index = thistype.m_missiles.backIndex()
428 return this
429 endmethod
430
431 public method onDestroy takes nothing returns nothing
432 //static members
433 call thistype.m_missiles.erase(this.m_index)
434 //members
435 if (this.m_unit != null) then
436 call RemoveUnit(this.m_unit)
437 endif
438 set this.m_unit = null
439 endmethod
440
441 private static method timerFunctionRefresh takes nothing returns nothing
442 local integer i = thistype.m_missiles.backIndex()
443 loop
444 exitwhen (i < 0)
445 if (not thistype(thistype.m_missiles[i]).m_isPaused) then
446 call thistype(thistype.m_missiles[i]).move()
447 endif
448 set i = i - 1
449 endloop
450 endmethod
451
452 public static method init takes real refreshTime, boolean enableCollisions returns nothing
453 debug if (refreshTime <= 0.0) then
454 debug call thistype.staticPrint("Wrong value refresh time value in AMissile struct initialization: " + R2S(refreshTime) + ".")
455 debug endif
456 //static start members
457 set thistype.m_refreshTime = refreshTime
458 set thistype.enableCollisions = enableCollisions
459 //static members
460 set thistype.m_missiles = AIntegerVector.create()
461 set thistype.m_refreshTimer = CreateTimer()
462 call TimerStart(thistype.m_refreshTimer, thistype.m_refreshTime, true, function thistype.timerFunctionRefresh)
463 endmethod
464
465 public static method cleanUp takes nothing returns nothing
466 call DestroyTimer(thistype.m_refreshTimer)
467 set thistype.m_refreshTimer = null
468 //remove all missiles
469 loop
470 exitwhen (thistype.m_missiles.empty())
471 call thistype(thistype.m_missiles.back()).destroy()
472 endloop
473 call thistype.m_missiles.destroy()
474 endmethod
475
476 public static method enable takes nothing returns nothing
477 call ResumeTimer(thistype.m_refreshTimer)
478 endmethod
479
480 public static method disable takes nothing returns nothing
481 call PauseTimer(thistype.m_refreshTimer)
482 endmethod
483
484 //dynamic static members
485
486 public static method refreshTime takes nothing returns real
487 return thistype.m_refreshTime
488 endmethod
489
490 //convenience methods
491 /*
492 public static method createCircle takes real x, real y, real z, real distance, integer count, boolean start, player owner, integer unitType, real speed, real damage, real damageRange, unit damageSource, attacktype attackType, damagetype damageType, weapontype weaponType, boolean collides, string deathEffectPath, string deathSoundPath returns AMissileVector
493 local AMissileVector vector = AMissileVector.create()
494 local thistype missile
495 local real angle = 0.0
496 local real angleValue = 360.0 / count
497 local integer i = 0
498 loop
499 exitwhen (i == count)
500 set missile = thistype.create()
501 call missile.setOwner(owner)
502 call missile.setUnitType(unitType)
503 call missile.setSpeed(speed)
504 call missile.setDamage(damage)
505 call missile.setDamageRange(damageRange)
506 call missile.setDamageSource(damageSource)
507 call missile.setAttackType(attackType)
508 call missile.setDamageType(damageType)
509 call missile.setWeaponType(weaponType)
510 call missile.setCollides(collides)
511 call missile.setTargetX(GetPolarProjectionX(x, angle, distance))
512 call missile.setTargetY(GetPolarProjectionY(y, angle, distance))
513 call missile.setDeathEffectPath(deathEffectPath)
514 call missile.setDeathSoundPath(deathSoundPath)
515 call vector.pushBack(missile)
516 if (start) then
517 call missile.start(x, y, z, angle)
518 endif
519 set angle = angle + angleValue
520 set i = i + 1
521 endloop
522 return missile
523 endmethod
524
525 public static method createCircleFromUnit takes unit usedUnit, real distance, integer count, boolean start, player owner, integer unitType, real speed, real damage, real damageRange, unit damageSource, attacktype attackType, damagetype damageType, weapontype weaponType, boolean collides, string deathEffectPath, string deathSoundPath returns AMissileVector
526 return thistype.createCircle(GetUnitX(usedUnit), GetUnitY(usedUnit), GetUnitZ(usedUnit), distance, count, start, owner, unitType, speed, damage, damageRange, damageSource, attackType, damageType, weaponType, collides, deathEffectPath, deathSoundPath)
527 endmethod
528 */
529 endstruct
530
531 endlibrary
532